﻿#include "study04.h"
// C/C++ 学习网站：https://legacy.cplusplus.com 注意旧版本（搜索）string

class KeyTest
{
private:
	string k_name;
	int k_age;
public:
	KeyTest()
		: k_name("KeyTest"), k_age(-1) {
	}

	KeyTest(string name)
		: k_name(name), k_age(-1) {
	}

	KeyTest(const string name, int age)
		: k_name(name), k_age(age) {
	}

	KeyTest(int age)
		: k_name("KeyTest"), k_age(age) {
	} //声明不要隐式转换的构造函数
protected: // 受保护的
	friend void print_KeyTest(const KeyTest& obj); // 声明友元函数
};

class KeyTest2
{
private:
	int k2_age;
public:
	KeyTest2()
		: k2_age(-1) {
	}
	explicit KeyTest2(int age) //声明不要隐式转换的构造函数
		: k2_age(age) {
	}
protected: // 受保护的
	friend void print_KeyTest(const KeyTest& obj); // 声明友元函数
};

static void print_KeyTest(const KeyTest& obj) { // 定义友元函数
	std::cout << "obj.k_name = " << obj.k_name
		<< ", obj.k_age = " << obj.k_age << std::endl;
}

static void print_ConstTest(ConstTest& const obj) { // 将 const 放 & 后面会有警告
	//*obj.in_x = 10; // 试图修改常量对象，不允许编译
	std::cout << "obj.in_x = " << obj.in_x
		<< ", obj.in_y = " << obj.in_y
		<< ", obj.val = " << obj.val << std::endl;
}

class ThisTest
{
private:
	const char* t_name;
	int x, y;
public:
	ThisTest(int x, int y) {
		t_name = "ThisTest";
		this->x = x; // 这里 x 同名了，如果不使用 this 关键字，则什么都不做 (默认都使用局部的 x)
		this->y = y;
		print_ThisTest(*this);
	}
	ThisTest(const char* name, int x, int y) {
		t_name = name;
		this->x = x;
		this->y = y;
		print_ThisTest(*this);
	}
protected:
	friend void print_ThisTest(const ThisTest& obj); // 声明友元函数
};

void print_ThisTest(const ThisTest& obj) {
	std::cout << "obj.t_name = " << obj.t_name
		<< ", obj.x = " << obj.x
		<< ", obj.y = " << obj.y << std::endl;
}

void test_key_word()
{
	// const 关键字
	{
		std::cout << "const 关键字测试:" << std::endl;
		const int MAX_NUM = 100;
		int num = 10;
		int* p0 = (int*)&MAX_NUM; // 指针变量
		const int* p1 = &num; // const 在 * 左边
		//*p1 = 20; //不能修改指针指向的值
		p1 = &MAX_NUM; //可以修改指针本身
		*p0 = 200; //强制修改常量，调试可以看到 MAX_NUM 的值被修改了，属于编译器未定义行为
		std::cout << "*p1 is: " << *p1 << "   MAX_NUM is: " << MAX_NUM
			<< "    *po is: " << *p0 << std::endl; // 打印 MAX_NUM = 100 编译器优化了代码
		std::cout << "po = " << p0 << "   &MAX_NUM = "
			<< &MAX_NUM << "   p1 = " << p1 << std::endl; //打印指针
		std::cout << "MAX_NUM + 10 = " << MAX_NUM + 10 << std::endl; // 常量表达式
		std::cout << "*p0 = " << *p0 << "   *p1 = " << *p1 << std::endl;//指针变量的值不变
		const int* const p2 = &num; // const 在 * 两边
		//*p2 = 30; //不能修改指针指向的值
		//p2 = &MAX_NUM; //不能修改指针本身

		// 测试类和 lambda 表达式里面的 mutable 关键字：
		ConstTest obj1;
		obj1.set_val(10);
		print_ConstTest(obj1);
		std::cout << "obj1.GetName() = " << obj1.GetName() << std::endl;
		int x = 0;
		auto add_add = [=]() mutable  // mutable 说明可以修改 lambda 表达式里面的变量
			{ // = 左值引用，即找到之前的所有变量，并将其值复制到当前 lambda 表达式的同名变量上
				++x; // = 左值引用，所以外层的 x 没有被修改了，但是 lambda 表达式里面的 x 被修改了
				std::cout << "add_add() called" << std::endl;
				std::cout << "in add_add x = " << x << std::endl; // 打印 x = 1
			}; // 匿名函数 (lambda表达式)
		add_add(); // 调用 lambda 表达式
		std::cout << "out add_add but in main x = " << x << std::endl; // 打印 x = 0
	}

	// new 关键字，实际上是一个操作符，使用 new 关键字创建的类会自动调用构造函数
	{
		std::cout << "new 关键字测试:" << std::endl;
		int* pi1 = new int(10); // 动态分配内存，并初始化为 10
		int* pi2 = new int[5]; // 动态分配内存，这里是一个数组，数组元素个数为 5
		// 上面一行代码相当于：int* pi2 = (int*)malloc(5 * sizeof(int));
		std::cout << "p1 = " << pi1 << "   *p1 = "
			<< *pi1 << "    sizeof p1 = " << sizeof(pi1) << std::endl;
		std::cout << "p2 = " << pi2 << "   sizeof p2 = " << std::endl;
		for (int i = 0; i < 5; ++i) {
			pi2[i] = (i + 1) * 2; // 数组元素赋值
			std::cout << "p2[" << i << "] = " << pi2[i] << "   ";
		}
		std::cout << std::endl;
		string* pc1 = new string[100]; // 分配内存大小为 100 个 sizeof(string) 字节

		delete pi1; // 相当于 free(fi1) 释放内存，注意 delete 释放的也是指针（至少释放 8 字节）
		delete[] pc1; // 释放内存，相当于 free 注意 delete[] 释放的是数组
		delete[] pi2; // 一个 delete 只能释放一个变量
	}

	// 隐式转换与 explicit 关键字
	{
		std::cout << "隐式转换与 explicit 关键字测试:" << std::endl;
		KeyTest obj1("obj1", 10); // 调用两个参数版本的构造函数
		KeyTest  obj2 = 20; // 隐式转换，调用 int 版本的构造函数，类似于 KeyTest obj2(20);
		//KeyTest obj2(20);  // 这里没有转换
		//KeyTest2 obj3 = 20; // err 无法调用 explicit 版本的构造函数
		//print_KeyTest("默认名字"); // err 发生两次隐式转换
		print_KeyTest(KeyTest("默认名字")); // ok
		print_KeyTest(obj1);
		print_KeyTest(obj2);
		// 调用 int 版本的构造函数，但是不允许隐式转换，所以下面代码会报错
	}

	// this 关键字
	{
		std::cout << "this 关键字测试:" << std::endl;
		ThisTest obj1(10, 20); // 调用两个参数版本的构造函数
		ThisTest obj2("obj2", 30, 40); // 调用三个参数版本的构造函数
	}
}